package com.ct.deadlockdetetor.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ct.deadlockdetetor.common.enums.ResultCode;
import com.ct.deadlockdetetor.common.exception.ServiceException;
import com.ct.deadlockdetetor.domain.allocation.ResourceAllocation;
import com.ct.deadlockdetetor.domain.deadLock.DeadLock;
import com.ct.deadlockdetetor.domain.deadLock.dto.DeadLockMessageQueryDTO;
import com.ct.deadlockdetetor.domain.deadLock.dto.DeleteProcessDTO;
import com.ct.deadlockdetetor.domain.deadLock.vo.DeadLockMessageVO;
import com.ct.deadlockdetetor.domain.resource.Resource;
import com.ct.deadlockdetetor.domain.wait.ProcessWait;
import com.ct.deadlockdetetor.mapper.DeadLockMapper;
import com.ct.deadlockdetetor.mapper.ProcessWaitMapper;
import com.ct.deadlockdetetor.mapper.ResourceAllocationMapper;
import com.ct.deadlockdetetor.mapper.ResourceMapper;
import com.ct.deadlockdetetor.service.DeadLockService;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Service
public class DeadLockServiceImpl implements DeadLockService {

    @Autowired
    private DeadLockMapper deadLockMapper;

    @Autowired
    private ResourceAllocationMapper resourceAllocationMapper;

    @Autowired
    private ProcessWaitMapper processWaitMapper;

    @Autowired
    private ResourceMapper resourceMapper;


    @Override
    public List<DeadLockMessageVO> list(DeadLockMessageQueryDTO deadLockMessageQueryDTO) {
        PageHelper.startPage(deadLockMessageQueryDTO.getPageNum(), deadLockMessageQueryDTO.getPageSize());
        return deadLockMapper.selectDeadLockMessageList(deadLockMessageQueryDTO);
    }

    @Override
    public boolean delete(Integer processId) {
        List<ProcessWait> processWaitList = processWaitMapper.selectByProcessId(processId);

        // 解除普通锁
//        if (processWaitList.isEmpty()) {
//            throw new ServiceException(ResultCode.PROCESS_ID_ERROR);
//        }
        log.info("进行死锁解除");
        for (ProcessWait processWait : processWaitList) {

            // 这里也需要删除等待中有关资源的死锁信息
            QueryWrapper<DeadLock> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda()
                    .eq(DeadLock::getResourceId, processWait.getResourceId());
            deadLockMapper.delete(queryWrapper);

            // 删除等待中的进程
            processWaitMapper.deleteById(processWait.getProcessWaitId());
        }

        // 释放实例
        releaseProcessResources(processId);
        return true;
    }

    // 释放占用的实例
    private void releaseProcessResources(Integer processId) {
        List<ResourceAllocation> resourceAllocationList = resourceAllocationMapper.selectByProcessId(processId);

        for (ResourceAllocation resourceAllocation : resourceAllocationList) {

            // 将删除的资源加回 resource 中
            UpdateWrapper<Resource> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda()
                    .eq(Resource::getResourceId, resourceAllocation.getResourceId())
                    .setSql("available_instances = available_instances + " + resourceAllocation.getAllocationInstances());
            resourceMapper.update(updateWrapper);

            // 通过 resourceId 删除死锁信息
            QueryWrapper<DeadLock> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda()
                    .eq(DeadLock::getResourceId, resourceAllocation.getResourceId());
            deadLockMapper.delete(queryWrapper);

            // 删除已经分配的进程
            resourceAllocationMapper.deleteById(resourceAllocation.getResourceAllocationId());

            // 通知等待中的进程
            notifyWaitingProcesses(resourceAllocation.getResourceId());
        }
    }

    // 通知等待中的进程
    private void notifyWaitingProcesses(Integer resourceId) {
        // 查找等待该资源的所有进程
        QueryWrapper<ProcessWait> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(ProcessWait::getResourceId, resourceId);
        List<ProcessWait> processWaitList = processWaitMapper.selectList(queryWrapper);

        for (ProcessWait processWait : processWaitList) {
            // 检查是否有足够资源分配给等待的进程
            Resource resource = resourceMapper.selectById(resourceId);
            if (resource != null && resource.getAvailableInstances() >= processWait.getRequestedInstances()) {
                // 尝试分配资源给等待的进程
                tryAllocateForWaitingProcess(processWait, resource);
            } else {

                // 为了确保顺序, 有一个不行就直接放弃唤醒后面的线程
                break;
            }
        }
    }

    // 为等待进程尝试分配资源
    private void tryAllocateForWaitingProcess(ProcessWait processWait, Resource resource) {
        // 更新资源可用实例数
        resource.setAvailableInstances(resource.getAvailableInstances() - processWait.getRequestedInstances());
        resourceMapper.updateById(resource);

        // 创建资源分配记录
        ResourceAllocation allocation = new ResourceAllocation();
        allocation.setResourceId(processWait.getResourceId());
        allocation.setProcessId(processWait.getProcessId());
        allocation.setAllocationInstances(processWait.getRequestedInstances());
        resourceAllocationMapper.insert(allocation);

        // 删除等待记录
        processWaitMapper.deleteById(processWait.getProcessWaitId());

        log.info("进程 {} 已获得所需资源 {}", processWait.getProcessId(), processWait.getResourceId());
    }
}
